#include <stdio.h>
#include <stdlib.h>

/* 邻接矩阵表示的图 */
typedef enum { false, true } bool;
typedef int Vertex; /* 顶点编号类型 */
typedef int GElemSet; /* 边权重类型 */
typedef struct MGraphNode *MGraph; /* 邻接矩阵表示的图 */
struct MGraphNode {
    int n_verts; /* 顶点数 */
    int m_edges; /* 边数 */
    GElemSet **dist_matrix;/* 距离矩阵 */
    GElemSet **cost_matrix;/* 费用矩阵 */
    Vertex s, d; /* s是出发地的城市编号；d是目的地的城市编号 */
    GElemSet no_edge_value; /* 表述没有边时的权重值 */
    bool directed; /* true为有向图，false为无向图 */
};
#define kMaxV 500   /* 最多顶点数为500 */
#define kMaxNum 1e9 /* 大于最大距离值的数字 */
void InitGraph(MGraph graph, int kMaxVertex, GElemSet no_edge_value,
               bool directed);
bool ExistEdge(MGraph graph, Vertex u, Vertex v);
void InsertEdge(MGraph graph, Vertex u, Vertex v, GElemSet dist, GElemSet cost);
MGraph BuildGraph();
/* 邻接矩阵表示的图 结束 */

void Dijkstra(MGraph graph, GElemSet dist[], GElemSet cost[]) {
    /* 求从graph->s到graph->d的最短距离，如果多解则选费用最少的 */
    int n, i;
    Vertex s, v, u, w;
    bool *collected;
    GElemSet min_dist;

    n = graph->n_verts; /* 顶点数 */
    s = graph->s; /* 源点 */
    collected = (bool *)malloc(sizeof(bool) * n);
    for (v = 0; v < n; v++) { /* 顶点信息初始化 */
        collected[v] = false;
        dist[v] = graph->dist_matrix[s][v]; /* 用源点s到其余各顶点的距离初始化dist */
        cost[v] = graph->cost_matrix[s][v]; /* 用源点s到其余各顶点的费用初始化cost */
    }
    collected[s] = true; /* 首先将s收入集合S */
    dist[s] = cost[s] = 0;
    /* 开始主循环，每次求得源点s到某个u顶点的最短路径，并加u到S集合 */
    for (i = 0; i < n; i++) {
        min_dist = kMaxNum;
        u = s; /* 初始化u为源点 */
        for (v = 0; v < n; v++) {
            if (collected[v] == false) { /* v顶点在V-S集合中 */
                if (dist[v] < min_dist) {
                    u = v;
                    min_dist = dist[v];
                }
            }
        } /* 找到离源点s最近的顶点u */
        collected[u] = true; /* 已经求得从s到u的最短路径 */
        if (u == graph->d)
            break; /* 如果已经求得起点到终点的最短距则跳出循环 */
        for (w = 0; w < n; w++) { /* 更新u的邻接点当前最短路径距离 */
            if (collected[w] == false) { /* 若u的邻接点w在V-S集合中 */
                if (dist[u] + graph->dist_matrix[u][w] < dist[w]) {
                    /* 若经过u的路径更短，则更新距离和费用 */
                    dist[w] = dist[u] + graph->dist_matrix[u][w];
                    cost[w] = cost[u] + graph->cost_matrix[u][w];
                } else if (dist[u] + graph->dist_matrix[u][w] == dist[w]
                           && cost[u] + graph->cost_matrix[u][w] < cost[w]) {
                    /* 若经过u的路径等长但费用更少，则更新费用 */
                    cost[w] = cost[u] + graph->cost_matrix[u][w];
                }
            }
        }
    }
}

int main(void) {
    MGraph graph;
    GElemSet *dist, *cost;

    graph = BuildGraph(); /* 根据输入创建图 */
    dist = (GElemSet *)malloc(sizeof(GElemSet) * graph->n_verts);
    cost = (GElemSet *)malloc(sizeof(GElemSet) * graph->n_verts);
    Dijkstra(graph, dist, cost); /* 用Dijkstra求解 */
    printf("%d %d\n", dist[graph->d], cost[graph->d]);

    return 0;
}

void InitGraph(MGraph graph, int kMaxVertex, GElemSet no_edge_value,
               bool directed) {
    /* 初始化一个空的图 */
    GElemSet *array;
    int i;
    Vertex u, v;

    graph->n_verts = 0;
    graph->m_edges = 0;
    /* 声明二维数组graph->dist_matrix[kMaxVertex][kMaxVertex] */
    array = (GElemSet *)malloc(sizeof(GElemSet) * kMaxVertex * kMaxVertex);
    graph->dist_matrix = (GElemSet **)malloc(sizeof(GElemSet *) * kMaxVertex);
    for (i = 0; i < kMaxVertex; i++) {
        graph->dist_matrix[i] = &array[i * kMaxVertex];
    }
    /* 声明二维数组graph->cost_matrix[kMaxVertex][kMaxVertex] */
    array = (GElemSet *)malloc(sizeof(GElemSet) * kMaxVertex * kMaxVertex);
    graph->cost_matrix = (GElemSet **)malloc(sizeof(GElemSet *) * kMaxVertex);
    for (i = 0; i < kMaxVertex; i++) {
        graph->cost_matrix[i] = &array[i * kMaxVertex];
    }
    graph->no_edge_value = no_edge_value;
    graph->directed = directed;
    for (u = 0; u < kMaxVertex; u++) {
        for (v = 0; v < kMaxVertex; v++) {
            graph->dist_matrix[u][v] = graph->no_edge_value;
            graph->cost_matrix[u][v] = graph->no_edge_value;
        }
    }
}

bool ExistEdge(MGraph graph, Vertex u, Vertex v) {
    bool ret = false;

    if (u < graph->n_verts && v < graph->n_verts) {
        if (u != v && graph->dist_matrix[u][v] != graph->no_edge_value) {
            ret = true;
        }
    }
    return ret;
}

void InsertEdge(MGraph graph, Vertex u, Vertex v, GElemSet dist,
                GElemSet cost) {
    if (ExistEdge(graph, u, v) == false) {
        graph->dist_matrix[u][v] = dist;
        graph->cost_matrix[u][v] = cost;
        graph->m_edges++;
        if (graph->directed == false) {
            graph->dist_matrix[v][u] = dist;
            graph->cost_matrix[v][u] = cost;
        }
    }
}

MGraph BuildGraph() {
    MGraph graph;
    int n, m, i;
    Vertex u, v;
    GElemSet dist, cost;

    graph = (MGraph)malloc(sizeof(struct MGraphNode));
    InitGraph(graph, kMaxV, kMaxNum, false);
    scanf("%d %d %d %d\n", &n, &m, &graph->s, &graph->d);
    graph->n_verts = n;
    for (i = 0; i < m; i++) {
        scanf("%d %d %d %d", &u, &v, &dist, &cost);
        InsertEdge(graph, u, v, dist, cost);
    }
    return graph;
}